﻿import mx.events.EventDispatcher;

import net.spreadshirt.shop.Service;

import net.spreadshirt.shop.lib.Session;
import net.spreadshirt.shop.lib.Basket;
import net.spreadshirt.shop.lib.Articles;
import net.spreadshirt.shop.lib.Designs;
import net.spreadshirt.shop.lib.Producttypes;
import net.spreadshirt.shop.lib.ProducttypeCategories;

import net.spreadshirt.logger.Logger;

/**
 * The Shop class represents the model of the mvc design pattern.
 * It creates and initializes the data architecture of the store.
 */
class net.spreadshirt.shop.Shop {

	// The shop variables.
	public var shop_id:Number;
	public var shop_lang:String;
	public var shop_locale:String;

	// The shop model objects.
	private var _service:Service;
	private var _session:Session;
	private var _articles:Articles;
	private var _designs:Designs;
	private var _producttypes:Producttypes;
	private var _producttype_categories:ProducttypeCategories;
	private var _basket:Basket;

	// Stores the error message.
	private var _errorMsg:String;

	/**
	 * Shop Constructor
	 */
	public function Shop(sid:Number, lang:String, locale:String) {
		shop_id = sid;
		shop_lang = lang;
		shop_locale = locale;
		// Initialize the service object.
		_service = new Service(
			shop_id,
			shop_lang,
			shop_locale
		);
		// Creates shop objects.
		_session = new Session();
		_articles = new Articles();
		_designs = new Designs();
		_producttypes = new Producttypes();
		_producttype_categories = new ProducttypeCategories();
		_basket = new Basket();
		// Initialize event dispatcher.
		EventDispatcher.initialize(this);
	}

	/**
	 * Establishes the webservice by initializing the session
	 * and receives the session id (token).
	 */
	public function init():Void {
		// note:	after receiving the token
		// 			the initData() method will be called
		// 			by initializeSession_respose()
		initializeSession();
	}

	/**
	 * Reset shop. Clear all shop data.
	 */
	public function reset():Void {
		Logger.getLog().info("SHOP - reseting data");
		// Clear value containers.
		_basket.clear();
		_producttype_categories.clear();
		_designs.clear();
		_articles.clear();
		_session.clear();
		// Reinitialize the webservice.
		_service = new Service(
			shop_id,
			shop_lang,
			shop_locale
		);
	}

	// The list of jobs to initialize the complete shop data.
	private static var JOBS:Array = [
		"getArticles",
		"getDesigns",
		"getProducttypes",
		"getProducttypeCategories",
		"getTNC"
	];
	private var _initDataJobList:Array;

	/**
	 * Start all jobs for loading the complete shop data.
	 */
	private function initData():Void {
		// Creates a copy of the job list.
		_initDataJobList = new Array();
		for(var i:Number = 0; i < JOBS.length; i++) {
			_initDataJobList.push(JOBS[i]);
		}
		// Starts all the jobs in the job list.
		Logger.getLog().info("SHOP - start initializing data");
		for(var n:Number = 0; n < _initDataJobList.length; n++) {
			this[_initDataJobList[n]]();
		}
	}

	/**
	 * Job is done. If all jobs are done, update view.
	 * 
	 * @param	jobName	The job name string.
	 */
	private function initDataDone(jobName:String):Void {
		for(var n:Number = 0; n < _initDataJobList.length; n++) {
			var method:String = _initDataJobList[n];
			if(jobName == method) {
				_initDataJobList.splice(n, 1);
			}
			if(_initDataJobList.length < 1) {
				Logger.getLog().info("SHOP - initializing data complete");
				updateArticles();
			}
		}
	}

	/**
	 * Declare event dispatcher methods.
	 */
	public function addEventListener(type:String, listener:Object):Void {
	}

	private function dispatchEvent(evt:Object):Void {
	}

	/**
	 * Handle Event: Articles are updated.
	 */
	private function updateArticles():Void {
		dispatchEvent({ type: "updateArticles" });
	}

	/**
	 * Handle Event: Basket is updated.
	 */
	private function updateBasket():Void {
		dispatchEvent({ type: "updateBasket" });
	}

	/**
	 * Handle Event: Loading data is in progress.
	 */
	private function startLoading():Void {
		dispatchEvent({ type: "startLoading" });
	}

	/**
	 * Handle Event: Loading data is complete.
	 */
	private function stopLoading():Void {
		dispatchEvent({ type: "stopLoading" });
	}

	/**
	 * Request single article item by article ID
	 *
	 * @param	article_id	The article ID.
	 * 
	 * @return	A single article item.
	 */
	public function getArticleItem(article_id:Number):Object {
		return _articles.getItem(article_id);
	}

	/**
	 * Returns a list of article items by category ID
	 *
	 * @param	category_id	A category ID.
	 * 
	 * @return	An array of article items.
	 */
	public function getArticleItemsByCategory(cat_id:Number):Array {
		var articles:Array = new Array();
		if(isNaN(cat_id) || cat_id < 0) {
			// Return all articles
			articles = _articles.getAllItems();
		} else {
			var ptids:Array = _producttypes.getIDsByCategory(cat_id);
			//trace("Shop.as ptids.length: "+ptids.length);
			for(var n:Number = 0; n < ptids.length; n++) {
				var items:Array = _articles.getItemsByProducttype(ptids[n]);
				articles = articles.concat(items);
			}
		}
		return articles;
	}

	/**
	 * Returns a list of design items.
	 *
	 * @return	An array of article items.
	 */
	public function getDesignItems():Array {
		return _designs.getAllItems();
	}

	/**
	 * Returns a single design item.
	 *
	 * @param	design_id	The design ID.
	 * @return	A design item.
	 */
	public function getDesignItem(design_id:Number):Object {
		return _designs.getItem(design_id);
	}

	/**
	 * Returns all product type category items.
	 * 
	 * @return	An array of product type category items.
	 */
	public function getCategoryItems():Array {
		var categories:Array = _producttype_categories.getAllItems();
		return categories;
	}

	/**
	 * Returns the terms and conditions.
	 *
	 * @return	 The terms and conditions html string.
	 */
	public function getTNCText():String {
		return _session.getTNC();
	}

	/**
	 * Service: Calls the submitOrder() method.
	 */
	public function submitOrder():Void {
		_service.submitOrder();
	}

	/**
	 * Service: initializeSession request
	 */
	public function initializeSession():Void {
		var callback:Object = {
			obj: this,
			fnc: "initializeSession_response"
		};
		Logger.getLog().info("SHOP - initializing session");
		_service.initializeSession(callback);
		startLoading();
	}

	/**
	 * Service: initializeSession response
	 */
	public function initializeSession_response(token:String):Void {
		stopLoading();
		_session.setToken(token);
		Logger.getLog().info("SHOP - session initialized: "
			+ _session.getToken());
		// start loading all data
		initData();
	}

	/**
	 * Service: updateSession request
	 */
	public function updateSession():Void {
		var callback:Object = {
			obj: this,
			fnc: "updateSession_response"
		};
		Logger.getLog().info("SHOP - updating session");
		_service.updateSession(callback);
		startLoading();
	}

	/**
	 * Service: updateSession response
	 */
	public function updateSession_response(
		token:String,
		lang:String,
		locale:String
	):Void {
		stopLoading();
		_session.setToken(token);
		_session.setLang(lang);
		_session.setLocale(locale);
		Logger.getLog().info("SHOP - session updated: "
			+ _session.getToken());
	}

	/**
	 * Service: getArticles request
	 */
	public function getArticles(
		producttype_id:Number,
		design_id:Number,
		position_id:Number,
		limit:Number,
		search_phrase:String
	):Void {
		var callback:Object = {
			obj: this,
			fnc: "getArticles_response"
		};
		Logger.getLog().info("SHOP - loading all articles");
		_service.getArticles(
			callback,
			producttype_id,
			design_id,
			position_id,
			limit,
			search_phrase
		);
		startLoading();
	}

	/**
	 * Service: getArticles response
	 */
	public function getArticles_response(items:Array):Void {
		stopLoading();
		_articles.addArticles(items);
		Logger.getLog().info("SHOP - articles received: "
			+ _articles.getCount());
		// remove from job list
		initDataDone("getArticles");
	}

	/**
	 * Service: getArticle request
	 */
	public function getArticle(article_id:Number):Void {
		var callback:Object = {
			obj: this,
			fnc: "getArticle_response"
		};
		Logger.getLog().info("SHOP - requesting article "
			+"[ID: "+ article_id +"]");
		_service.getArticle(callback, article_id);
		startLoading();
	}

	/**
	 * Service: getArticle response
	 */
	public function getArticle_response(item:Object):Void {
		stopLoading();
		Logger.getLog().info("SHOP - single article: "
			+ item.article_id);
		// 
		// handle the requested item here.
		// var yourItem:Object = item;
		//
	}

	/**
	 * Service: addBasketitem request
	 */
	public function addBasketitem(
		article_id:Number,
		quantity:Number,
		producttype_size_id:Number,
		producttype_color_id:Number
	):Void {
		var callback:Object = {
			obj: this,
			fnc: "addBasketitem_response"
		};
		Logger.getLog().info("SHOP - adding item to basket");
		_service.addBasketitem(
			callback,
			article_id,
			quantity,
			producttype_size_id,
			producttype_color_id
		);
		startLoading();
	}

	/**
	 * Service: addBasketItem response
	 */
	public function addBasketitem_response(basketitem_id:Number):Void {
		stopLoading();
		Logger.getLog().info("SHOP - basket item added: "+ basketitem_id);
		// request for complete basket content
		this.getBasket();
	}

	/**
	 * Service: updateBasketitem request
	 */
	public function updateBasketitem(
		basketitem_id:Number,
		quantity:Number,
		producttype_size_id:Number,
		producttype_color_id:Number
	):Void {
		var callback:Object = {
			obj: this,
			fnc: "updateBasketitem_response"
		};
		Logger.getLog().info("SHOP - updating basket item");
		_service.updateBasketitem(
			callback,
			basketitem_id,
			quantity,
			producttype_size_id,
			producttype_color_id
		);
		startLoading();
	}

	/**
	 * Service: updateBasketitem response
	 */
	public function updateBasketitem_response(basketitem_id:Number):Void {
		stopLoading();
		Logger.getLog().info("SHOP - basket item updated: "
			+ basketitem_id);
		// request for complete basket content
		getBasket();
	}

	/**
	 * Service: getBasket request
	 */
	public function getBasket():Void {
		var callback:Object = {
			obj: this,
			fnc: "getBasket_response"
		};
		Logger.getLog().info("SHOP - loading basket");
		_service.getBasket(callback);
		startLoading();
	}

	/**
	 * Service: getBasket - response
	 */
	public function getBasket_response(basket:Object):Void {
		stopLoading();
		_basket.setBasket(basket);
		Logger.getLog().info("SHOP - basket items received: "
			+ _basket.getCount());
		updateBasket();
	}

	/**
	 * Service: getDesigns request
	 */
	public function getDesigns(
		producttype_id:Number,
		position:Number,
		limit:Number,
		search_phrase:String
	):Void {
		var callback:Object = {
			obj: this,
			fnc: "getDesigns_response"
		};
		Logger.getLog().info("SHOP - loading all designs");
		_service.getDesigns(
			callback,
			producttype_id,
			position,
			limit,
			search_phrase
		);
		startLoading();
	}

	/**
	 * Service: getDesigns response
	 */
	public function getDesigns_response(items:Array):Void {
		stopLoading();
		_designs.addDesigns(items);
		Logger.getLog().info("SHOP - designs received: "
			+ _designs.getCount());// + ":  " + items[0].design_id.toString());
		// remove from job list
		initDataDone("getDesigns");
	}

	/**
	 * Service: getProducttypes request
	 */
	public function getProducttypes(
		position:Number,
		limit:Number,
		search_phrase:String
	):Void {
		var callback:Object = {
			obj: this,
			fnc: "getProducttypes_response"
		};
		Logger.getLog().info("SHOP - loading all types of products");
		_service.getProducttypes(
			callback,
			position,
			limit,
			search_phrase
		);
		startLoading();
	}

	/**
	 * Service: getProducttypes response
	 */
	public function getProducttypes_response(items:Array):Void {
		stopLoading();
		_producttypes.addProducttypes(items);
		Logger.getLog().info("SHOP - producttypes received: "
			+ _producttypes.getCount());
		// remove from job list
		initDataDone("getProducttypes");
	}

	/**
	 * Service: getProducttypeCategories request
	 */
	public function getProducttypeCategories(
		position:Number,
		limit:Number,
		search_phrase:String
	):Void {
		var callback:Object = {
			obj: this,
			fnc: "getProducttypeCategories_response"
		};
		Logger.getLog().info("SHOP - loading all categories");
		_service.getProducttypeCategories(
			callback,
			position,
			limit,
			search_phrase
		);
		startLoading();
	}

	/**
	 * Service: getProductTypeCategories response
	 */
	public function getProducttypeCategories_response(items:Array):Void {
		stopLoading();
		_producttype_categories.addProducttypeCategories(items);
		Logger.getLog().info("SHOP - producttypeCategories received: "
			+ _producttype_categories.getCount());
		// remove from job list
		initDataDone("getProducttypeCategories");
	}

	/**
	 * Service: redeemCoupon request
	 */
	public function redeemCoupon(coupon_code:String):Void {
		var callback:Object = {
			obj: this,
			fnc: "redeemCoupon_response"
		};
		Logger.getLog().info("SHOP - sending coupon code to redeem");
		_service.redeemCoupon(callback, coupon_code);
		startLoading();
	}

	/**
	 * Service: redeemCoupon response
	 */
	public function redeemCoupon_response(isOk:Boolean):Void {
		stopLoading();
		if(isOk) {
			// Increase the number of redeemed coupons.
			_session.increaseNumberOfCoupons();
		}
		Logger.getLog().info("SHOP - number of coupons redeemed: "
			+ _session.getCouponCount());
	}

	/**
	 * Service: getTNC request
	 */
	public function getTNC():Void {
		var callback:Object = {
			obj: this,
			fnc: "getTNC_response"
		};
		Logger.getLog().info("SHOP - loading terms and conditions");
		_service.getTNC(callback);
		startLoading();
	}

	/**
	 * Service: getTNC response
	 */
	public function getTNC_response(str:String):Void {
		stopLoading();
		_session.setTNC(str);
		Logger.getLog().info("SHOP - terms and conditions received");
		// remove from job list
		initDataDone("getTNC");
	}

	/**
	 * Service: acceptTNC request
	 */
	public function acceptTNC():Void {
		var callback:Object = {
			obj: this,
			fnc: "acceptTNC_response"
		};
		var isAccepted:Boolean = _session.getTNCAccepted();
		if(!isAccepted) {
			Logger.getLog().info("SHOP - accepting terms and conditions");
			_service.acceptTNC(callback);
			startLoading();
		}
	}

	/**
	 * Service: acceptTNC response
	 */
	public function acceptTNC_response(accepted:Boolean):Void {
		stopLoading();
		_session.setTNCAccepted(accepted);
		var isAccepted:Boolean = _session.getTNCAccepted();
		Logger.getLog().info("SHOP - acceptTNC received: "+ isAccepted);
	}

}
